#include <jni.h>
#include <string>
#include <android/log.h>
#include <unistd.h>
#include <dlfcn.h>
#include <__mutex/mutex.h>
#include "libunwind.h"
#include "func-stack-backtrace.h"



typedef struct {
    uint64_t regs[32];
} regs_ctx_t;


extern "C" void hook_entry(void);


#define LOG_TAG "Backtrace"
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

void get_fp(unsigned long *fp) {
#ifdef __aarch64__
    __asm__ __volatile__ (
    "mov %0, x29 \t\n"
    :"=r"(*fp));
#else
    __asm__ __volatile__ (
            "mov %0, r7 \t\n"
            :"=r"(*fp));
#endif
}

std::mutex backtrace_mutex;

uint64_t get_reg_x(void *ctx, int index) {
    uint64_t raw = ((regs_ctx_t *)ctx)->regs[index];

    if (index == 8) {
        return raw & 0xFFF;     // syscall id
    }

    if (index == 31) {
        return ((uint64_t)ctx) + 32 * 8;    // return sp
    }

    return raw;
}

// -------------------- libunwind native trace --------------------

void libunwind_native_backtrace(void *ctx) {
    LOGD("begin_native_backtrace");

//    backtrace_mutex.lock();

    unw_context_t context;
    unw_cursor_t cursor;

    if (unw_getcontext(&context) != 0) {
        LOGD("unw ctx fail");
        return;
    }

    if (unw_init_local(&cursor, &context) != 0) {
        LOGD("[BigBrother] unw init fail");
        return;
    }
    // 设置寄存器参数，主要作用是限制unwind 回溯堆栈的起始位置，如果直接使用，那么寄存器是当前函数栈的
    unw_set_reg(&cursor, UNW_ARM64_X0, get_reg_x(ctx, 0));
    unw_set_reg(&cursor, UNW_ARM64_X1, get_reg_x(ctx, 1));
    unw_set_reg(&cursor, UNW_ARM64_X2, get_reg_x(ctx, 2));
    unw_set_reg(&cursor, UNW_ARM64_X3, get_reg_x(ctx, 3));
    unw_set_reg(&cursor, UNW_ARM64_X4, get_reg_x(ctx, 4));
    unw_set_reg(&cursor, UNW_ARM64_X5, get_reg_x(ctx, 5));
    unw_set_reg(&cursor, UNW_ARM64_X6, get_reg_x(ctx, 6));
    unw_set_reg(&cursor, UNW_ARM64_X7, get_reg_x(ctx, 7));
    unw_set_reg(&cursor, UNW_ARM64_X8, get_reg_x(ctx, 8));
    unw_set_reg(&cursor, UNW_ARM64_X9, get_reg_x(ctx, 9));
    unw_set_reg(&cursor, UNW_ARM64_X10, get_reg_x(ctx, 10));
    unw_set_reg(&cursor, UNW_ARM64_X11, get_reg_x(ctx, 11));
    unw_set_reg(&cursor, UNW_ARM64_X12, get_reg_x(ctx, 12));
    unw_set_reg(&cursor, UNW_ARM64_X13, get_reg_x(ctx, 13));
    unw_set_reg(&cursor, UNW_ARM64_X14, get_reg_x(ctx, 14));
    unw_set_reg(&cursor, UNW_ARM64_X15, get_reg_x(ctx, 15));
    unw_set_reg(&cursor, UNW_ARM64_X16, get_reg_x(ctx, 16));
    unw_set_reg(&cursor, UNW_ARM64_X17, get_reg_x(ctx, 17));
    unw_set_reg(&cursor, UNW_ARM64_X18, get_reg_x(ctx, 18));
    unw_set_reg(&cursor, UNW_ARM64_X19, get_reg_x(ctx, 19));
    unw_set_reg(&cursor, UNW_ARM64_X20, get_reg_x(ctx, 20));
    unw_set_reg(&cursor, UNW_ARM64_X21, get_reg_x(ctx, 21));
    unw_set_reg(&cursor, UNW_ARM64_X22, get_reg_x(ctx, 22));
    unw_set_reg(&cursor, UNW_ARM64_X23, get_reg_x(ctx, 23));
    unw_set_reg(&cursor, UNW_ARM64_X24, get_reg_x(ctx, 24));
    unw_set_reg(&cursor, UNW_ARM64_X25, get_reg_x(ctx, 25));
    unw_set_reg(&cursor, UNW_ARM64_X26, get_reg_x(ctx, 26));
    unw_set_reg(&cursor, UNW_ARM64_X27, get_reg_x(ctx, 27));
    unw_set_reg(&cursor, UNW_ARM64_X28, get_reg_x(ctx, 28));
    unw_set_reg(&cursor, UNW_ARM64_X29, get_reg_x(ctx, 29));
    unw_set_reg(&cursor, UNW_ARM64_X30, get_reg_x(ctx, 30));
    unw_set_reg(&cursor, UNW_ARM64_X31, get_reg_x(ctx, 31));

    // unw_set_reg(&cursor, UNW_ARM64_PC, get_pc(ctx));

    // for (int i = 0; i < 1; i++) {
    while (true) {
        unw_word_t ip, sp, off;
        unw_word_t func_addr;
        char sym_name[256] = "\0";

        if (unw_step(&cursor) <= 0) {
            LOGD("unw step fail");
            break;
        }

        if (unw_get_reg(&cursor, UNW_REG_IP, &ip) != 0) {
            LOGD("unw get pc fail");
            break;
        }

        unw_get_proc_name(&cursor, sym_name, 256, &off);

        LOGD("backtrace : ip=0x%lx  off=0x%lx",ip, off);
        Dl_info info;
        if(dladdr((const void *)ip,&info)){
            LOGD("so name : %s",info.dli_fname);
            LOGD("load addr : %lx",info.dli_fbase);
            LOGD("fun : %s",info.dli_sname);
            LOGD("dli_saddr : %lx",info.dli_saddr);

        }
        // if (strcmp(sym_name, "JVM_NativeLoad") == 0) {
        //     break;
        // }
    }

    LOGD("end_native_backtrace");

//    backtrace_mutex.unlock();

    return;
}



uint64_t get_pc(void *ctx) {
    return ((regs_ctx_t *)ctx)->regs[8] >> 12;
}
extern "C" void user_hook(regs_ctx_t *ctx) {


    LOGD("enter user_hook");
    uint64_t syscall_id = get_reg_x(ctx, 8);
    uint64_t ret_addr = get_pc(ctx);

    try {
        libunwind_native_backtrace(ctx);
    }catch (...) {

        return;
    }
    LOGD("leave user_hook");
    return;
}




void text_backtrace1(int b){
    int a = 10;
//    LOGD("text_backtrace %d",a+b);
    hook_entry();
}

void text_backtrace(int b){
    int a = 10;
//    LOGD("text_backtrace %d",a+b);
    text_backtrace1(a);
}

void text_backtrace2(int b){
    int a = 10;
    LOGD("text_backtrace %d",a+b);
    func_backtrace();
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_alibaba_unwind_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";

    //汇编的形式栈回溯，如果中间加上text_backtrace 会报错
    text_backtrace(100);

//    text_backtrace2(100);
    return env->NewStringUTF(hello.c_str());
}
